#include <Engine/StdAfx.h>
#include <Engine/Renderer/D3D11/RendererD3D11.h>
#include <Engine/Foundation/Object.h>
#include <Engine/GlobalEnv.h>
#include <Engine/IEngine.h>
#include <Engine/ICamera.h>
#include <Engine/Renderer/IRenderState.h>
#include <Engine/Renderer/ILight.h>
#include <Engine/Renderer/D3D11/VertexBufferD3D11.h>
#include <Engine/Renderer/D3D11/IndexBufferD3D11.h>
#include <Engine/Renderer/D3D11/ShaderD3D11.h>
#include <Engine/Renderer/D3D11/TextureD3D11.h>
#include <Engine/Renderer/D3D11/ConvertEnumD3D11.h>
#include <Engine/Renderer/D3D11/ConvertStructD3D11.h>
#include <Engine/Renderer/D3D11/InputLayoutD3D11.h>
#include <Engine/Renderer/D3D11/RenderStateD3D11.h>
#include <Engine/Misc/StringUtils.h>
#include <d3d11.h>
#include <d3dcompiler.h>

#define _RENDERER_FRAME_PROFILER_

using namespace fastbird;

//----------------------------------------------------------------------------
IRenderer* IRenderer::CreateD3D11Instance()
{
	return new RendererD3D11();
}

//----------------------------------------------------------------------------
RendererD3D11::RendererD3D11()
{
	m_pDevice = 0;
	m_pFactory = 0;
	m_pSwapChain = 0;
	m_pImmediateContext = 0;
	m_pRenderTargetView = 0;
	m_pDepthStencil = 0;
	m_pDepthStencilView = 0;
	m_pFrameConstantsBuffer = 0;
	m_pObjectConstantsBuffer = 0;
	m_pMaterialConstantsBuffer = 0;
	m_pMaterialParametersBuffer = 0;
	m_pWireframeRasterizeState = 0;
	mForcedWireframe = false;

	mWidth = 0;
	mHeight = 0;
	mMultiSampleDesc.Count = 4;
	mMultiSampleDesc.Quality = 16;
	mDepthStencilCreated = false;
	mDepthStencilFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
	mFrameConstants.gViewProj.MakeIdentity();
	mCamera = 0;
}

//----------------------------------------------------------------------------
RendererD3D11::~RendererD3D11()
{	
	{
		auto it = mRasterizerMap.begin(), itEnd = mRasterizerMap.end();
		for (; it!=itEnd; it++)
		{
			SAFE_RELEASE(it->second);
		}
	}
	{
		auto it = mBlendMap.begin(), itEnd = mBlendMap.end();
		for(; it!=itEnd; it++)
		{
			SAFE_RELEASE(it->second);
		}
	}
	{
		auto it = mSamplerMap.begin(), itEnd = mSamplerMap.end();
		for(; it!=itEnd; it++)
		{
			SAFE_RELEASE(it->second);
		}
	}

	SAFE_RELEASE(m_pWireframeRasterizeState);
	SAFE_RELEASE(m_pMaterialParametersBuffer);
	SAFE_RELEASE(m_pMaterialConstantsBuffer);
	SAFE_RELEASE(m_pObjectConstantsBuffer);
	SAFE_RELEASE(m_pFrameConstantsBuffer);
	SAFE_RELEASE(m_pDepthStencilView);
	SAFE_RELEASE(m_pDepthStencil);
	SAFE_RELEASE(m_pRenderTargetView);
	SAFE_RELEASE(m_pImmediateContext);
	SAFE_RELEASE(m_pSwapChain);
	SAFE_RELEASE(m_pDevice);
}

//----------------------------------------------------------------------------
bool RendererD3D11::Init()
{
	HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&m_pFactory);
	if (FAILED(hr))
	{
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, "CreateDXGIFactory1() failed!");
		return false;
	}

	
	UINT i = 0; 
	IDXGIAdapter1 * pAdapter; 
	std::vector <IDXGIAdapter1*> vAdapters; 
	while(m_pFactory->EnumAdapters1(i, &pAdapter) != DXGI_ERROR_NOT_FOUND) 
	{ 
		vAdapters.push_back(pAdapter); 
		DXGI_ADAPTER_DESC1 adapterDesc;
		pAdapter->GetDesc1(&adapterDesc);
		++i; 
	}

	RECT rc;
	GetClientRect(gEnv->pEngine->GetWindowHandle(), &rc);
	int width = rc.right - rc.left;
	int height = rc.bottom - rc.top;

	UINT createDeviceFlags = 0;
#ifdef _DEBUG
//		createDeviceFlags = D3D11_CREATE_DEVICE_DEBUG;
#endif

	D3D_DRIVER_TYPE driverTypes[] = 
	{
		D3D_DRIVER_TYPE_UNKNOWN,
		D3D_DRIVER_TYPE_HARDWARE,
		D3D_DRIVER_TYPE_WARP,
		D3D_DRIVER_TYPE_REFERENCE
	};
	int numDriverTypes = _ARRAYSIZE(driverTypes);

	D3D_FEATURE_LEVEL featureLevels[] = 
	{
		D3D_FEATURE_LEVEL_11_0,
		D3D_FEATURE_LEVEL_10_1,
		D3D_FEATURE_LEVEL_10_0,
		D3D_FEATURE_LEVEL_9_3
	};
	int numFeatureLevels = _ARRAYSIZE(featureLevels);

	for( int driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
	{
		mDriverType = driverTypes[driverTypeIndex];
		hr = D3D11CreateDevice(vAdapters[0], mDriverType, NULL, createDeviceFlags, featureLevels, numFeatureLevels,
			D3D11_SDK_VERSION, &m_pDevice, &mFeatureLevel, &m_pImmediateContext);
		if (SUCCEEDED(hr))
			break;
	}

	if (FAILED( hr ))
	{
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, "D3D11CreateDevice() failed!");
		return false;
	}


	mMultiSampleDesc.Count = 1;
	mMultiSampleDesc.Quality = 0;
	unsigned msQuality = 0;
	
	hr = m_pDevice->CheckMultisampleQualityLevels(DXGI_FORMAT_R8G8B8A8_UNORM, 4, &msQuality);
	if (SUCCEEDED(hr))
	{
		mMultiSampleDesc.Count = 4;
		mMultiSampleDesc.Quality = msQuality-1;
	}

	DXGI_SWAP_CHAIN_DESC sd={};
	sd.BufferCount = 1;
	sd.BufferDesc.Width = width;
	sd.BufferDesc.Height = height;
	sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	sd.BufferDesc.RefreshRate.Numerator = 60;
	sd.BufferDesc.RefreshRate.Denominator = 1;
	sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	sd.OutputWindow = gEnv->pEngine->GetWindowHandle();
	sd.SampleDesc = mMultiSampleDesc;
	sd.Windowed = true;

	hr = m_pFactory->CreateSwapChain(m_pDevice, &sd, &m_pSwapChain);
	if (FAILED(hr))
	{
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, "CreateSwapChain failed!");
		return false;
	}
	/*
	for( int driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
	{
		mDriverType = driverTypes[driverTypeIndex];
		hr = D3D11CreateDeviceAndSwapChain(NULL, mDriverType, NULL, createDeviceFlags, featureLevels, numFeatureLevels,
										D3D11_SDK_VERSION, &sd, &m_pSwapChain, &m_pDevice, &mFeatureLevel, &m_pImmediateContext);
		if( SUCCEEDED(hr))
			break;
	}
	if (FAILED( hr ))
	{
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, "Failed to initialize D3D11!");
		return false;
	}
	*/
	// RenderTargetView
	ID3D11Texture2D* pBackBuffer = NULL;
	hr = m_pSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), (void**)&pBackBuffer);
	if (FAILED(hr))
	{
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, "Failed to get backbuffer!");
		return false;
	}
	hr = m_pDevice->CreateRenderTargetView( pBackBuffer, NULL, &m_pRenderTargetView);
	pBackBuffer->Release();
	if (FAILED(hr))
	{
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, "Failed to create a render target view!");
		return false;
	}

	// DepthStencilView
	D3D11_TEXTURE2D_DESC depthStencilDesc;
	depthStencilDesc.Width = width;
	depthStencilDesc.Height = height;
	depthStencilDesc.MipLevels = 1;
	depthStencilDesc.ArraySize = 1;
	depthStencilDesc.Format = mDepthStencilFormat;
	depthStencilDesc.SampleDesc = mMultiSampleDesc;
	depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
	depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	depthStencilDesc.CPUAccessFlags = 0;
	depthStencilDesc.MiscFlags = 0;	
	hr = m_pDevice->CreateTexture2D(&depthStencilDesc, 0, &m_pDepthStencil);
	if (FAILED(hr))
	{
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, "Failed to create the depth stencil texture!");
	}
	else
	{
		D3D11_DEPTH_STENCIL_VIEW_DESC viewDesc;
		viewDesc.Format = mDepthStencilFormat;
		if (mMultiSampleDesc.Count==1)
		{
			viewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
		}
		else
		{
			viewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
		}
		viewDesc.Flags = 0;
		viewDesc.Texture2D.MipSlice = 0;
		hr = m_pDevice->CreateDepthStencilView(m_pDepthStencil, &viewDesc, &m_pDepthStencilView);
		if (FAILED(hr))
		{
			IEngine::Log(FB_DEFAULT_DEBUG_ARG, "Failed to create the depth stencil view!");
		}
		else
		{
			mDepthStencilCreated = true;
		}
	}
	m_pImmediateContext->OMSetRenderTargets(1, &m_pRenderTargetView, m_pDepthStencilView);

	D3D11_VIEWPORT vp;
	vp.Width = (float)width;
	vp.Height = (float)height;
	vp.MinDepth = 0.f;
	vp.MaxDepth = 1.f;
	vp.TopLeftX = 0;
	vp.TopLeftY = 0;
	m_pImmediateContext->RSSetViewports(1, &vp);
	mWidth = width;
	mHeight = height;

	//------------------------------------------------------------------------
	// FRAME CONSTANT
	D3D11_BUFFER_DESC Desc;
	Desc.Usage = D3D11_USAGE_DYNAMIC;
	Desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	Desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	Desc.MiscFlags = 0;    
	Desc.ByteWidth = sizeof( FRAME_CONSTANTS );
	hr = m_pDevice->CreateBuffer( &Desc, NULL, &m_pFrameConstantsBuffer );
	if (FAILED( hr ) )
	{
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, "Failed to create constant buffer(FrameConstants)!");
		assert(0);
	}
	mFrameConstants.gViewProj.MakeIdentity();
	mFrameConstants.gDirectionalLightDir_Intensity = Vec4(1.f, 1.f, 1.f, 1.f);
	mFrameConstants.gDirectionalLightDiffuse = Vec4(1.f, 1.f, 1.f, 1.f);
	mFrameConstants.gDirectionalLightSpecular = Vec4(1.f, 1.f, 1.f, 1.f);
	mFrameConstants.gCameraPos = Vec4(0.f, 0.f, 2.0f, 1.0f);
	D3D11_MAPPED_SUBRESOURCE mappedResource;
	hr = m_pImmediateContext->Map(m_pFrameConstantsBuffer, 0, D3D11_MAP_WRITE_DISCARD, 
		ConvertEnumD3D11(MAP_FLAG_NONE), &mappedResource);
	if (FAILED(hr))
		assert(0);
	memcpy(mappedResource.pData, &mFrameConstants, sizeof(FRAME_CONSTANTS));
	m_pImmediateContext->Unmap(m_pFrameConstantsBuffer, 0);

	//------------------------------------------------------------------------
	// OBJECT CONSTANT
	Desc.ByteWidth = sizeof( OBJECT_CONSTANTS );
	hr = m_pDevice->CreateBuffer( &Desc, NULL, &m_pObjectConstantsBuffer );
	if ( FAILED( hr ) )
	{
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, "Failed to create constant buffer(ObjectConstants)!");
		assert(0);
	}

	//------------------------------------------------------------------------
	// MATERIAL CONSTANT
	Desc.ByteWidth = sizeof( MATERIAL_CONSTANTS );
	hr = m_pDevice->CreateBuffer( &Desc, NULL, &m_pMaterialConstantsBuffer );
	if ( FAILED( hr ) )
	{
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, "Failed to create constant buffer(ObjectConstants)!");
		assert(0);
	}

	//------------------------------------------------------------------------
	// MATERIAL PARAMETERS
	Desc.ByteWidth = sizeof( MATERIAL_PARAMETERS );
	hr = m_pDevice->CreateBuffer( &Desc, NULL, &m_pMaterialParametersBuffer );
	if ( FAILED( hr ) )
	{
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, "Failed to create constant buffer(ObjectConstants)!");
		assert(0);
	}

	//------------------------------------------------------------------------
	// Wireframe Render state
	D3D11_RASTERIZER_DESC RasterizerDesc;
	RasterizerDesc.FillMode = D3D11_FILL_WIREFRAME;
	RasterizerDesc.CullMode = D3D11_CULL_NONE;
	RasterizerDesc.FrontCounterClockwise = FALSE;
	RasterizerDesc.DepthBias = 0;
	RasterizerDesc.DepthBiasClamp = 0.0f;
	RasterizerDesc.SlopeScaledDepthBias = 0.0f;
	RasterizerDesc.DepthClipEnable = TRUE;
	RasterizerDesc.ScissorEnable = FALSE;
	RasterizerDesc.MultisampleEnable = FALSE;
	RasterizerDesc.AntialiasedLineEnable = FALSE;
	hr = m_pDevice->CreateRasterizerState( &RasterizerDesc, &m_pWireframeRasterizeState );
	if (FAILED(hr))
	{
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, "Failed to create an wireframe rasterizer state!");
		assert(0);
	}

	// Light
	mDirectionalLight = ILight::CreateLight(ILight::LIGHT_TYPE_DIRECTIONAL);
	mDirectionalLight->SetPosition(Vec3(1, 1, 1));
	mDirectionalLight->SetDiffuse(Vec3(1, 1, 1));
	mDirectionalLight->SetSpecular(Vec3(1, 1, 1));
	mDirectionalLight->SetIntensity(1.0f);

	return true;
}

//----------------------------------------------------------------------------
void RendererD3D11::Deinit()
{
	SAFE_RELEASE(m_pFrameConstantsBuffer);
	SAFE_RELEASE(m_pRenderTargetView);
	SAFE_RELEASE(m_pImmediateContext);
	SAFE_RELEASE(m_pSwapChain);
	SAFE_RELEASE(m_pDevice);
}

//----------------------------------------------------------------------------
void RendererD3D11::Clear(float r, float g, float b, float z)
{
	float ClearColor[4] = { r, g, b, 1.0f }; // red,green,blue,alpha
	m_pImmediateContext->ClearRenderTargetView( m_pRenderTargetView, ClearColor );
	m_pImmediateContext->ClearDepthStencilView( m_pDepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, z, 0 );
}

//----------------------------------------------------------------------------
void RendererD3D11::SetCamera(ICamera* pCamera)
{
	mCamera = pCamera;
}

//----------------------------------------------------------------------------
ICamera* RendererD3D11::GetCamera() const
{
	return mCamera;
}

//----------------------------------------------------------------------------
void RendererD3D11::UpdateFrameConstantsBuffer()
{
	if (mCamera)
	{
		mFrameConstants.gViewProj = mCamera->GetViewProjMat();
	}
	else
	{
		assert(0);
	}
	mFrameConstants.gDirectionalLightDir_Intensity = float4(mDirectionalLight->GetPosition(), mDirectionalLight->GetIntensity());
	mFrameConstants.gDirectionalLightDiffuse = float4(mDirectionalLight->GetDiffuse(), 1.0f);
	mFrameConstants.gDirectionalLightSpecular = float4(mDirectionalLight->GetSpecular(), 1.0f);
	mFrameConstants.gCameraPos = float4(mCamera->GetPos(), 1.0f);
	D3D11_MAPPED_SUBRESOURCE mappedResource;
	m_pImmediateContext->Map( m_pFrameConstantsBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource );
	memcpy(mappedResource.pData, &mFrameConstants, sizeof(FRAME_CONSTANTS));
	m_pImmediateContext->Unmap(m_pFrameConstantsBuffer, 0);
	m_pImmediateContext->VSSetConstantBuffers(0, 1, &m_pFrameConstantsBuffer);
	m_pImmediateContext->PSSetConstantBuffers(0, 1, &m_pFrameConstantsBuffer);
}

//----------------------------------------------------------------------------
void RendererD3D11::UpdateObjectConstantsBuffer(void* pData)
{
	D3D11_MAPPED_SUBRESOURCE mappedResource;
	m_pImmediateContext->Map( m_pObjectConstantsBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource );
	memcpy(mappedResource.pData, pData, sizeof(OBJECT_CONSTANTS));
	m_pImmediateContext->Unmap(m_pObjectConstantsBuffer, 0);
	m_pImmediateContext->VSSetConstantBuffers(1, 1, &m_pObjectConstantsBuffer);
}

//----------------------------------------------------------------------------
void RendererD3D11::UpdateMaterialConstantsBuffer(void* pData)
{
	D3D11_MAPPED_SUBRESOURCE mappedResource;
	m_pImmediateContext->Map( m_pMaterialConstantsBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource );
	memcpy(mappedResource.pData, pData, sizeof(OBJECT_CONSTANTS));
	m_pImmediateContext->Unmap(m_pMaterialConstantsBuffer, 0);
	m_pImmediateContext->VSSetConstantBuffers(2, 1, &m_pMaterialConstantsBuffer);
}

//----------------------------------------------------------------------------
void* RendererD3D11::MapMaterialParameterBuffer()
{
	D3D11_MAPPED_SUBRESOURCE mappedResource;
	m_pImmediateContext->Map( m_pMaterialParametersBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource );
	return mappedResource.pData;
	
}

//----------------------------------------------------------------------------
void RendererD3D11::UnmapMaterialParameterBuffer()
{
	m_pImmediateContext->Unmap(m_pMaterialParametersBuffer, 0);
	m_pImmediateContext->VSSetConstantBuffers(3, 1, &m_pMaterialParametersBuffer);
	m_pImmediateContext->PSSetConstantBuffers(3, 1, &m_pMaterialParametersBuffer);
}

//----------------------------------------------------------------------------
void RendererD3D11::Present()
{
	HRESULT hr = m_pSwapChain->Present(1, 0);
	assert(!FAILED(hr));
}

//----------------------------------------------------------------------------
void RendererD3D11::SetVertexBuffer(unsigned int startSlot, unsigned int numBuffers,
	IVertexBuffer* pVertexBuffers[], unsigned int strides[], unsigned int offsets[])
{
	ID3D11Buffer* pHardwareBuffers[32];
	for (int i=0; i<(int)numBuffers; i++)
	{
		assert(pVertexBuffers[i]);
		pHardwareBuffers[i] = static_cast<VertexBufferD3D11*>(pVertexBuffers[i])->GetHardwareBuffer();
	}
	m_pImmediateContext->IASetVertexBuffers(startSlot, numBuffers, pHardwareBuffers, strides, offsets);
}

//----------------------------------------------------------------------------
void RendererD3D11::SetIndexBuffer(IIndexBuffer* pIndexBuffer)
{
	IndexBufferD3D11* pIndexBufferD3D11 = static_cast<IndexBufferD3D11*>(pIndexBuffer);
	ID3D11Buffer* pHardwareBuffer = pIndexBufferD3D11->GetHardwareBuffer();
	m_pImmediateContext->IASetIndexBuffer(pHardwareBuffer, pIndexBufferD3D11->GetFormatD3D11(), pIndexBufferD3D11->GetOffset());
}

//----------------------------------------------------------------------------
void RendererD3D11::SetTexture(ITexture* pTexture, BINDING_SHADER shaderType, unsigned int slot)
{
	TextureD3D11* pTextureD3D11 = static_cast<TextureD3D11*>(pTexture);
	
	ID3D11ShaderResourceView* pSRV = pTextureD3D11->GetHardwareResourceView();
	ID3D11SamplerState* pSamplerState = pTextureD3D11->GetSamplerState();
	switch(shaderType)
	{
	case BINDING_SHADER_VERTEX_SHADER:
		m_pImmediateContext->VSSetShaderResources(slot, 1, &pSRV);
		m_pImmediateContext->VSSetSamplers(slot, 1, &pSamplerState);
		break;
	case BINDING_SHADER_PIXEL_SHADER:
		m_pImmediateContext->PSSetShaderResources(slot, 1, &pSRV);
		m_pImmediateContext->PSSetSamplers(slot, 1, &pSamplerState);
		break;
	default:
		assert(0);
		break;	
	}
}

//----------------------------------------------------------------------------
IVertexBuffer* RendererD3D11::CreateVertexBuffer(void* data, unsigned stride, 
	unsigned numVertices, BUFFER_USAGE usage, BUFFER_CPU_ACCESS_FLAG accessFlag)
{
	if (usage == BUFFER_USAGE_IMMUTABLE && data==0)
	{
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, "Failed to create vertex buffer! "
			"Immutable needs data pointer.");
		assert(0);
		return 0;		
	}

	VertexBufferD3D11* pVertexBufferD3D11 = VertexBufferD3D11::CreateInstance(stride, numVertices);
	assert(pVertexBufferD3D11);

	D3D11_BUFFER_DESC bufferDesc;
	bufferDesc.Usage = ConvertEnumD3D11(usage);
	bufferDesc.ByteWidth = stride * numVertices;
	bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bufferDesc.CPUAccessFlags = ConvertEnumD3D11(accessFlag);
	bufferDesc.MiscFlags = 0;

	D3D11_SUBRESOURCE_DATA initData;
	initData.pSysMem = data;
	initData.SysMemPitch = 0;
	initData.SysMemSlicePitch = 0;

	ID3D11Buffer* pHardwareBuffer = 0;
	HRESULT hr;
	if (data)
	{
		hr = m_pDevice->CreateBuffer(&bufferDesc, &initData, &pHardwareBuffer);	
	}
	else
	{
		hr = m_pDevice->CreateBuffer(&bufferDesc, 0, &pHardwareBuffer);
	}

	if (FAILED(hr))
	{
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, "Failed to create a vertex buffer!");
	}
	

	pVertexBufferD3D11->SetHardwareBuffer(pHardwareBuffer);

	return pVertexBufferD3D11;
}

//----------------------------------------------------------------------------
IIndexBuffer* RendererD3D11::CreateIndexBuffer(void* data, unsigned int numIndices, INDEXBUFFER_FORMAT format)
{
	IndexBufferD3D11* pIndexBufferD3D11 = IndexBufferD3D11::CreateInstance(numIndices, format);
	assert(pIndexBufferD3D11);

	D3D11_BUFFER_DESC bufferDesc;
	bufferDesc.Usage = D3D11_USAGE_DEFAULT;
	bufferDesc.ByteWidth = pIndexBufferD3D11->GetSize();
	bufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bufferDesc.CPUAccessFlags = 0;
	bufferDesc.MiscFlags = 0;

	D3D11_SUBRESOURCE_DATA initData;
	initData.pSysMem = data;
	initData.SysMemPitch = 0;
	initData.SysMemSlicePitch = 0;

	ID3D11Buffer* pHardwareBuffer = 0;
	HRESULT hr = m_pDevice->CreateBuffer(&bufferDesc, &initData, &pHardwareBuffer);
	if (FAILED(hr))
	{
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, "Failed to create a index buffer!");
	}
	
	pIndexBufferD3D11->SetHardwareBuffer(pHardwareBuffer);

	return pIndexBufferD3D11;
}

//----------------------------------------------------------------------------
HRESULT CompileShaderFromFile(const char* filename, const char* entryPoint, const char* shaderModel, ID3DBlob** ppBlobOut, D3D_SHADER_MACRO* pDefines = 0)
{
	HRESULT hr = S_OK;
	DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined(DEBUG) || defined(_DEBUG)
	dwShaderFlags |= D3DCOMPILE_DEBUG;
#endif

	ID3DBlob* pErrorBlob = 0;
	hr = D3DX11CompileFromFile( filename, pDefines, 0, entryPoint, shaderModel, dwShaderFlags, 0,
		0, ppBlobOut, &pErrorBlob, 0);
	if (FAILED(hr))
	{
		if (pErrorBlob)
		{
			IEngine::Log(FB_DEFAULT_DEBUG_ARG, "[Error] CompileShaderFromFile %s failed!", filename);
			IEngine::Log((const char*)pErrorBlob->GetBufferPointer());
		}
	}
	if (pErrorBlob)
		pErrorBlob->Release();
	return hr;
}

//----------------------------------------------------------------------------
IShader* RendererD3D11::CreateShader(const char* filepath, IShader* pReloadingShader/*=0*/)
{
	ShaderD3D11* pShader;
	if (pReloadingShader)
	{
		pShader = (ShaderD3D11*)pReloadingShader;
		assert(strcmp(filepath,pShader->GetName())==0);
	}
	else
	{
		IShader* pShaderFromMemory = Shader::FindShader(filepath);
		if (pShaderFromMemory)
			return pShaderFromMemory;

		pShader = ShaderD3D11::CreateInstance(filepath);
	}
	char onlyname[MAX_PATH];
	strcpy_s( onlyname, MAX_PATH, StripPath(filepath) );
	StripExtension(onlyname);

	// Load VS
	std::string VSName = onlyname;
	VSName+="_VertexShader";
	ID3DBlob* pVSBlob = 0;
	HRESULT hr = CompileShaderFromFile(filepath, VSName.c_str(), "vs_5_0", &pVSBlob);
	if (FAILED(hr))
	{		
		SAFE_RELEASE(pVSBlob);
		return pShader;
	}

	// Create VS
	ID3D11VertexShader* pVertexShader = 0;
	hr = m_pDevice->CreateVertexShader(pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), 0, &pVertexShader);
	if (FAILED(hr))
	{
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, pVSBlob->GetBufferPointer());
		SAFE_RELEASE(pVSBlob);
		return pShader;
	}
	pShader->SetVertexShader(pVertexShader);
	pShader->SetVertexShaderBytecode(pVSBlob);

	// Load PS
	std::string PSName = onlyname;
	PSName+="_PixelShader";
	ID3DBlob* pPSBlob = 0;
	hr = CompileShaderFromFile(filepath, PSName.c_str(), "ps_5_0", &pPSBlob);
	if (FAILED(hr))
	{
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, pPSBlob->GetBufferPointer());
		SAFE_RELEASE(pPSBlob);
		return pShader;
	}

	// Create PS
	ID3D11PixelShader* pPixelShader = 0;
	hr = m_pDevice->CreatePixelShader( pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), 0, &pPixelShader);
	if (FAILED(hr))
	{
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, pPSBlob->GetBufferPointer());
		SAFE_RELEASE(pPSBlob);
		return pShader;
	}
	pShader->SetPixelShader(pPixelShader);
	SAFE_RELEASE(pPSBlob);

	return pShader;
}

//----------------------------------------------------------------------------
ITexture* RendererD3D11::CreateTexture(const Vec2I& size, int mipLevels, int arraySize)
{
	assert(0);
	return 0;
}

//----------------------------------------------------------------------------
ITexture* RendererD3D11::CreateTexture(const char* file, ITexture* pReloadingTexture/*=0*/)
{	
	ID3D11ShaderResourceView* pSRView = 0;
	HRESULT hr = D3DX11CreateShaderResourceViewFromFile(m_pDevice, file, 0, 0, &pSRView, 0);
	if (FAILED(hr))
	{
		IEngine::Log("[Error] Failed to load a texture (%s).", file);		
	}

	TextureD3D11* pTexture = 0;
	if (pReloadingTexture)
		pTexture = (TextureD3D11*)pReloadingTexture;
	else
		pTexture = TextureD3D11::CreateInstance();

	pTexture->SetHardwareResourceView(pSRView);

	pTexture->SetName(file);

	return pTexture;
	
	/*
	FIBITMAP* pImage = LoadImage(file, 0);
	if (!pImage)
	{
		assert(pImage);
		return pTexture;
	}
	
	FREE_IMAGE_TYPE type = FreeImage_GetImageType(pImage);

	unsigned bpp = FreeImage_GetBPP(pImage);
	unsigned width = FreeImage_GetWidth(pImage);
	unsigned height = FreeImage_GetHeight(pImage);
	unsigned line = FreeImage_GetLine(pImage); // width in bytes
	unsigned pitch = FreeImage_GetPitch(pImage);
	ID3D11Texture2D *pTextureD3D11 = NULL;

	switch(type)
	{
	case FIT_BITMAP:
		{
			D3D11_TEXTURE2D_DESC desc;
			desc.Width = width;
			desc.Height = height;
			desc.MipLevels = desc.ArraySize = 1;
			desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
			desc.SampleDesc.Count = 1;
			desc.Usage = D3D11_USAGE_IMMUTABLE;
			desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
			desc.CPUAccessFlags = 0;

			RGBQUAD* pSrc;
			if ( bpp == 32)
			{				
				pSrc = (RGBQUAD*)FreeImage_GetBits(pImage);
			}
			else if (bpp == 24)
			{
				pSrc = new RGBQUAD[width*height];
				RGBQUAD* pSrcIt = pSrc;
				for(unsigned y = 0; y < height; y++) 
				{
					RGBTRIPLE* bits = (RGBTRIPLE*)FreeImage_GetScanLine(pImage, y);
					for(unsigned x = 0; x < width; x++) 
					{
						pSrcIt->rgbBlue = bits->rgbtBlue;
						pSrcIt->rgbGreen = bits->rgbtGreen;
						pSrcIt->rgbRed = bits->rgbtRed;
						pSrcIt->rgbReserved = 255;
						// jump to next pixel
						pSrcIt++;
						bits++;						
					}
				}
			}

			D3D11_SUBRESOURCE_DATA sd;
			sd.pSysMem  = pSrc;
			sd.SysMemPitch = pitch;
			sd.SysMemSlicePitch = 0;
			m_pDevice->CreateTexture2D( &desc, &sd, &pTextureD3D11 );
		}
		break;

	default:
		assert(0);
	}
	pTexture->SetHardwareTexture(pTextureD3D11);

	FreeImage_Unload(pImage);
	*/
}

//----------------------------------------------------------------------------
ITexture* RendererD3D11::CreateTexture(void* data, int bpp, int width, int height)
{
	ID3D11Texture2D *pTextureD3D11 = NULL;
	D3D11_TEXTURE2D_DESC desc;
	desc.Width = width;
	desc.Height = height;
	desc.MipLevels = desc.ArraySize = 1;
	desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	desc.SampleDesc.Count = 1;
	desc.SampleDesc.Quality = 0;
	desc.Usage = D3D11_USAGE_IMMUTABLE;
	desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	desc.CPUAccessFlags = 0;
	desc.MiscFlags = 0;
	D3D11_SUBRESOURCE_DATA sd;
	sd.pSysMem  = data;
	sd.SysMemPitch = width * bpp/8;
	sd.SysMemSlicePitch = 0;
	HRESULT hr;
	if (FAILED(hr = m_pDevice->CreateTexture2D( &desc, &sd, &pTextureD3D11 )))
	{
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, "Failed to CreateTexture from memory!");
		assert(0);
		return 0;
	}

	TextureD3D11* pTexture = TextureD3D11::CreateInstance();
	pTexture->SetHardwareTexture(pTextureD3D11);

	D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
	srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	srvDesc.Texture2D.MostDetailedMip = 0;
	srvDesc.Texture2D.MipLevels=1;
	ID3D11ShaderResourceView* pResourceView;
	m_pDevice->CreateShaderResourceView(pTextureD3D11, &srvDesc, &pResourceView);

	pTexture->SetHardwareResourceView(pResourceView);

	return pTexture;

}

//----------------------------------------------------------------------------
IInputLayout* RendererD3D11::CreateInputLayout(INPUT_ELEMENT_DESC desc[], unsigned count, 
	const void* pShaderBytecodeWithInputSignature, unsigned byteLength)
{
	assert(pShaderBytecodeWithInputSignature);
	InputLayoutD3D11* pInputLayoutD3D11 = InputLayoutD3D11::CreateInstance();
	std::vector<D3D11_INPUT_ELEMENT_DESC> d3d11Descs;
	d3d11Descs.resize(count);
	for (unsigned i=0; i<count; i++)
	{
		d3d11Descs[i] = ConvertStructD3D11(desc[i]);
	}
	ID3D11InputLayout* pHardwareInputLayout=0;
	
	HRESULT hr = m_pDevice->CreateInputLayout(&d3d11Descs[0], count, pShaderBytecodeWithInputSignature, byteLength, &pHardwareInputLayout);
	if (FAILED(hr))
	{
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, "Failed to create input layout!");
	}
	pInputLayoutD3D11->SetHardwareInputLayout(pHardwareInputLayout);

	return pInputLayoutD3D11;
}

//----------------------------------------------------------------------------
void RendererD3D11::SetShader(IShader* pShader)
{
	if (!pShader->IsValid())
	{
		assert(0 && "Shader is not valid!");
		return;
	}

	ShaderD3D11* pShaderD3D11 = static_cast<ShaderD3D11*>(pShader);	
	ID3D11VertexShader* pVS = pShaderD3D11->GetVertexShader();
	m_pImmediateContext->VSSetShader(pVS, 0, 0);
	ID3D11HullShader* pHS = pShaderD3D11->GetHullShader();
	m_pImmediateContext->HSSetShader(pHS, 0, 0);
	ID3D11DomainShader* pDS = pShaderD3D11->GetDomainShader();
	m_pImmediateContext->DSSetShader(pDS, 0, 0);
	ID3D11PixelShader* pPS = pShaderD3D11->GetPixelShader();
	m_pImmediateContext->PSSetShader(pPS, 0, 0);
}

//----------------------------------------------------------------------------
void RendererD3D11::SetInputLayout(IInputLayout* pInputLayout)
{
	InputLayoutD3D11* pInputLayoutD3D11 = static_cast<InputLayoutD3D11*>(pInputLayout);
	ID3D11InputLayout* pLayout = pInputLayoutD3D11->GetHardwareInputLayout();
	assert(pLayout);
	m_pImmediateContext->IASetInputLayout(pLayout);
}

//----------------------------------------------------------------------------
void RendererD3D11::SetPrimitiveTopology(PRIMITIVE_TOPOLOGY pt)
{
	D3D11_PRIMITIVE_TOPOLOGY d3d11PT;
	switch(pt)
	{
	case PRIMITIVE_TOPOLOGY_POINTLIST:
		d3d11PT = D3D11_PRIMITIVE_TOPOLOGY_POINTLIST;
		break;
	case PRIMITIVE_TOPOLOGY_LINELIST:
		d3d11PT = D3D11_PRIMITIVE_TOPOLOGY_LINELIST;
		break;
	case PRIMITIVE_TOPOLOGY_LINESTRIP:
		d3d11PT = D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP;
		break;
	case PRIMITIVE_TOPOLOGY_TRIANGLELIST:
		d3d11PT = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
		break;
	case PRIMITIVE_TOPOLOGY_TRIANGLESTRIP:
		d3d11PT = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
		break;
	default:
		IEngine::Log(FB_DEFAULT_DEBUG_ARG, "Undefined primitive topology!");
		assert(0);
		d3d11PT = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;
	}

	m_pImmediateContext->IASetPrimitiveTopology(d3d11PT);
}

//----------------------------------------------------------------------------
void RendererD3D11::DrawIndexed(unsigned indexCount, 
	unsigned startIndexLocation, unsigned startVertexLocation)
{
	m_pImmediateContext->DrawIndexed(indexCount, startIndexLocation, startVertexLocation);

#ifdef _RENDERER_FRAME_PROFILER_
	mFrameProfiler.NumDrawIndexedCall++;
	mFrameProfiler.NumIndexCount += indexCount;
#endif
}

//----------------------------------------------------------------------------
void RendererD3D11::Draw(unsigned int vertexCount, unsigned int startVertexLocation)
{
	m_pImmediateContext->Draw(vertexCount, startVertexLocation);

#ifdef _RENDERER_FRAME_PROFILER_
	mFrameProfiler.NumDrawCall++;
	mFrameProfiler.NumVertexCount += vertexCount;
#endif
}

//----------------------------------------------------------------------------
void RendererD3D11::SetWireframe(bool enable)
{
	assert(m_pWireframeRasterizeState);
	if(mForcedWireframe != enable)
	{
		mForcedWireframe = enable;
		if (mForcedWireframe)
		{
			m_pImmediateContext->RSSetState(m_pWireframeRasterizeState);
		}
		else
		{
			m_pImmediateContext->RSSetState(0);
		}
	}
}

//----------------------------------------------------------------------------
MapData RendererD3D11::MapVertexBuffer(IVertexBuffer* pBuffer, UINT subResource, 
	MAP_TYPE type, MAP_FLAG flag)
{
	VertexBufferD3D11* pD3D11VB = dynamic_cast<VertexBufferD3D11*>(pBuffer);
	assert(pD3D11VB);
	
	D3D11_MAPPED_SUBRESOURCE mappedSubresource;
	HRESULT hr = m_pImmediateContext->Map(pD3D11VB->GetHardwareBuffer(), subResource, ConvertEnumD3D11(type),
		ConvertEnumD3D11(flag), &mappedSubresource);
	if (FAILED(hr))
	{
		assert(0);
		MapData data;
		data.pData = 0;
		return data;
	}

	MapData data;
	data.pData = mappedSubresource.pData;
	data.rowPitch = mappedSubresource.RowPitch;
	data.depthPitch = mappedSubresource.DepthPitch;

	return data;
}

//----------------------------------------------------------------------------
void RendererD3D11::UnmapVertexBuffer(IVertexBuffer* pBuffer, unsigned int subResource)
{
	VertexBufferD3D11* pBufferD3D11 = dynamic_cast<VertexBufferD3D11*>(pBuffer);
	assert(pBufferD3D11);
	m_pImmediateContext->Unmap(pBufferD3D11->GetHardwareBuffer(), subResource);
}

//----------------------------------------------------------------------------
void RendererD3D11::FindOrCreateSamplerState(ITexture* pTexture, const SAMPLER_DESC& desc)
{
	TextureD3D11* pD3D11Texture = static_cast<TextureD3D11*>(pTexture);
	auto find = mSamplerMap.find(desc);
	if (find == mSamplerMap.end())
	{
		ID3D11SamplerState* pSamplerState = 0;
		D3D11_SAMPLER_DESC d3d11desc;
		ConvertStructD3D11(d3d11desc, desc);
		HRESULT hr = m_pDevice->CreateSamplerState(&d3d11desc, &pSamplerState);
		if (FAILED(hr))
		{
			IEngine::Log(FB_DEFAULT_DEBUG_ARG, "Failed to create sampler state!");
			assert(0);
		}
		mSamplerMap[desc] = pSamplerState;
		pSamplerState->AddRef();
		pD3D11Texture->SetSamplerState(pSamplerState);
		return;
	}
	find->second->AddRef();
	pD3D11Texture->SetSamplerState(find->second);
}

//----------------------------------------------------------------------------
void RendererD3D11::FindOrCreateRasterizerState(IObject* pObject, const RASTERIZER_DESC& desc)
{
	auto find = mRasterizerMap.find(desc);
	Object* pObj = static_cast<Object*>(pObject);
	RasterizerStateD3D11* pRasterizerStateD3D11 = (RasterizerStateD3D11*)pObj->GetRasterizerState();

	if (find == mRasterizerMap.end())
	{
		ID3D11RasterizerState* pRasterizerState = 0;
		D3D11_RASTERIZER_DESC d3d11desc;
		ConvertStructD3D11(d3d11desc, desc);
		HRESULT hr = m_pDevice->CreateRasterizerState( &d3d11desc, &pRasterizerState );
		if (FAILED(hr))
		{
			IEngine::Log(FB_DEFAULT_DEBUG_ARG, "Failed to create an rasterizer state!");
			assert(0);
		}
		mRasterizerMap[desc] = pRasterizerState;
		pRasterizerState->AddRef();		
		pRasterizerStateD3D11->SetHardwareRasterizerState(pRasterizerState);
		return;
	}
	find->second->AddRef();
	pRasterizerStateD3D11->SetHardwareRasterizerState(find->second);
}

//----------------------------------------------------------------------------
void RendererD3D11::FindOrCreateBlendState(IObject* pObject, const BLEND_DESC& desc)
{
	auto find = mBlendMap.find(desc);
	Object* pObj = static_cast<Object*>(pObject);
	BlendStateD3D11* pBlendStateD3D11 = (BlendStateD3D11*)pObj->GetBlenderState();

	if (find == mBlendMap.end())
	{
		ID3D11BlendState* pBlendState = 0;
		D3D11_BLEND_DESC d3d11desc;
		ConvertStructD3D11(d3d11desc, desc);
		HRESULT hr = m_pDevice->CreateBlendState( &d3d11desc, &pBlendState );
		if (FAILED(hr))
		{
			IEngine::Log(FB_DEFAULT_DEBUG_ARG, "Failed to create a blend state!");
			assert(0);
		}
		mBlendMap[desc] = pBlendState;		
		pBlendState->AddRef();
		pBlendStateD3D11->SetHardwareBlendState(pBlendState);
		return;
	}
	find->second->AddRef();
	pBlendStateD3D11->SetHardwareBlendState(find->second);
}

//----------------------------------------------------------------------------
void RendererD3D11::SetRasterizerState(IRasterizerState* pRasterizerState)
{	
	if (!mForcedWireframe)
	{
		RasterizerStateD3D11* pRasterizerStateD3D11 = (RasterizerStateD3D11*)pRasterizerState;
		m_pImmediateContext->RSSetState(pRasterizerStateD3D11->GetHardwareRasterizerState());
	}
}

//----------------------------------------------------------------------------
void RendererD3D11::SetBlendState(IBlendState* pBlendState)
{
	BlendStateD3D11* pBlendStateD3D11 = (BlendStateD3D11*)pBlendState;
	m_pImmediateContext->OMSetBlendState(pBlendStateD3D11->GetHardwareBlendState(), 
		pBlendStateD3D11->GetBlendFactor(), pBlendStateD3D11->GetBlendMask());
}

//----------------------------------------------------------------------------
void RendererD3D11::InitFrameProfiler(float dt)
{
	mFrameProfiler.Clear();
	mFrameProfiler.UpdateFrameRate(dt);
}

//----------------------------------------------------------------------------
const RENDERER_FRAME_PROFILER& RendererD3D11::GetFrameProfiler() const
{
	return mFrameProfiler;
}